#!/usr/bin/env escript
%% -*- erlang-indent-level: 4; indent-tabs-mode: nil -*-

-mode(compile).

%% See https://hexdocs.pm/argparse/
%% Argparse supports a hierarchical structure of commands and subcommands, each
%% with its own arguments. This spec (no command specified) describes the top level.
%%
%% We only provide the user-level arguments. The node connection arguments, which
%% are automatically provided by the script wrappers, are specified in aeu_ext_scripts.erl
spec() -> #{ arguments => args() }.

args() ->
    [ #{ name => tree
       , short => $t
       , long => "-tree"
       , required => false
       , type => {string, ["all", "accounts", "calls", "contracts", "oracles", "channels", "ns"]}
       , help => "Tree to do a safe access scan on"
       , default => "all"}
    ].


main(Args) ->
    Opts = aeu_ext_scripts:parse_opts(Args, spec()),
    {ok, Node} = aeu_ext_scripts:connect_node(Opts),
    db_safe_access_scan(Node, Opts).

db_safe_access_scan(Node, #{opts := Opts}) ->
    case rpc:call(Node, aec_db_gc, gc_enabled, []) of
        false ->
            case Opts of
                #{tree := "all"} ->
                    db_safe_access_scan_(Node, [accounts, calls, contracts, oracles, channels, ns]);
                #{tree := Tree} ->
                    db_safe_access_scan_(Node, [list_to_atom(Tree)])
            end;
        _ ->
            io:fwrite("Aborted! Scan is only available when Garbage Collect is OFF!\n", [])
    end.

db_safe_access_scan_(_Node, []) ->
    io:fwrite("\nScan COMPLETE\n");
db_safe_access_scan_(Node, [Tree | Trees]) ->
    scan_tree(Node, Tree),
    db_safe_access_scan_(Node, Trees).

scan_tree(Node, Tree) ->
    io:fwrite("Start scan of ~p\n.", [Tree]),
    Self = self(),
    spawn(fun() ->
              Res = rpc:call(Node, aec_db_gc, db_safe_access_scan, [Tree]),
              Self ! {res, Tree, Res}
          end),
    wait_for_scan(Tree, erlang:system_time(millisecond)).

wait_for_scan(Tree, Start) ->
    receive {res, Tree, Res} ->
        case Res of
            {ok, N} ->
                io:fwrite("\nScan of ~p SUCCESSFUL took ~.1fs - checked ~p DB nodes\n",
                          [Tree, elapsed(Start), N]);
            {error, Reason} ->
                io:fwrite("\nScan of ~p FAILED - reasons ~p\n", [Tree, Reason])
        end
    after 3000 ->
        io:fwrite(".", []),
        wait_for_scan(Tree, Start)
    end.

elapsed(Start) ->
    (erlang:system_time(millisecond) - Start) / 1000.
